/**
 * FIFS - The Find It File System
 * Copyright (C) 2006 Chris Oklota (chris at oklota dot net)
 * 
 * This program is distributed under the terms of the GNU LGPL.
 * See the file http://www.gnu.org/licenses/lgpl.txt
 */
package fifs;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


/**
 * This class is a mock implementation of the backing database for use in
 * unit tests of the Fifs Filesystem interface. It will keep all files in
 * memory structures.
 */
public class FifsMockDaoImpl implements FifsDao {

	private HashMap<Long, FifsFile> files = new HashMap<Long, FifsFile>();
	private HashMap<Long, ArrayList<Byte>> fileContents = new HashMap<Long, ArrayList<Byte>>();
	private Set<String> tags = new HashSet<String>();
	private HashMap<Long, Set<String>> tagAssc = new HashMap<Long, Set<String>>();
	private long nextFh = 1;
	
	
	public FifsMockDaoImpl() {
	
	}

	public boolean createFile(FifsFile file) {
		long fh = nextFh++;
		file.setFh(fh);
		files.put(fh, file);
		fileContents.put(fh, new ArrayList<Byte>());
		tags.addAll(file.getTags());
		tagAssc.put(fh, file.getTags());
		return true;
	}

    public boolean deleteFile(FifsFile file) {
        long fh = file.getFh();
        tagAssc.remove(fh);
        fileContents.remove(fh);
        files.remove(fh);
        return true;
    }
    
	public boolean createTag(String tag) {
		tags.add(tag);
		return true;
	}

    public boolean deleteTag(String tag) {
        tags.remove(tag);
        return true;
    }
    
	public Set<FifsFile> getAllFilesByTags(Set<String> tags) {
		HashSet<FifsFile> rs = new HashSet<FifsFile>();
		for (FifsFile f : files.values()) {
			if (f.getTags().containsAll(tags)) {
				rs.add(f);
			}
		}
		return rs;
	}

	public Set<String> getAllTags() {
		return tags;
	}

	public FifsFile getFile(Set<String> tags, String filename) {
		for (FifsFile f : files.values()) {
			if (f.getTags().containsAll(tags) && 
				f.getFilename().equals(filename)) {
				return f;
			}
		}
		return null;
	}

	public FifsFile getFile(long fh) {
		return files.get(fh);
	}

    public boolean setFileSize(long fh, long size) {
        ArrayList<Byte> data = fileContents.get(fh);
        if (data == null) {
            return false;
        }
        int currentSize = data.size();
        if (size > currentSize) {
            for (int i = currentSize; i < size; i++) {
                data.add((byte)0);
            }
        } else if (size < currentSize) {
            for (int i = currentSize - 1; i > size - 1; i--) {
                data.remove(i);
            }
        }
        return true;
    }
    
	public boolean updateFileAttributes(FifsFile file) {
		files.put(file.getFh(), file);
		return true;
	}

    public boolean updateFileTags(FifsFile file) {
        long fh = file.getFh();
        Set<String> newTags = file.getTags();
        tagAssc.put(fh, newTags);
        return true;
    }
    
	public byte[] getFileBytes(long fh, long offset, long length) {
		ArrayList<Byte> data = fileContents.get(fh);
		if (data != null) {
			if ((offset < 0) || (offset > data.size()-1)) {
				// offset not within data range, return empty byte array
				return new byte[0];
			}
			// determine end read index, either offset+length or data size
			long endIdx = (offset + length > data.size()) ? data.size() : (int)offset + length;
			List<Byte> sublist = data.subList((int)offset, (int)endIdx);
			// create a byte array to return, and copy the bytes from the content list
			byte[] bytes = new byte[sublist.size()];
			for (int i = 0; i < bytes.length; i++) {
				bytes[i] = sublist.get(i);
			}
			return bytes;
		}
		return null;
	}
	
	public boolean writeFileBytes(long fh, long offset, byte[] bytes) {
		ArrayList<Byte> data = fileContents.get(fh);
		if (data != null) {
			int currIdx = data.size();
			// pad the data with nulls if needed to get to offset
			while (currIdx < offset) {
				data.add((byte)0);
				currIdx++;
			}
			currIdx = (int)offset;
			for (int i=0; i < bytes.length; i++) {
				if (currIdx + i >= data.size()) {
					data.add(bytes[i]);
				} else {
					data.set(currIdx + i, bytes[i]);
				}
			}
			return true;
		}
		return false;
	}

}
